home *** CD-ROM | disk | FTP | other *** search
/ Gekikoh Dennoh Club 5 / Gekikoh Dennoh Club Vol. 5 (Japan).7z / Gekikoh Dennoh Club Vol. 5 (Japan) (Track 01).bin / internet / xip / iijppp.lzh / src / main.c < prev    next >
C/C++ Source or Header  |  1994-10-21  |  15KB  |  692 lines

  1. /*
  2.  *            User Process PPP
  3.  *
  4.  *        Written by Toshiharu OHNO (tony-o@iij.ad.jp)
  5.  *
  6.  *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
  7.  *
  8.  * Redistribution and use in source and binary forms are permitted
  9.  * provided that the above copyright notice and this paragraph are
  10.  * duplicated in all such forms and that any documentation,
  11.  * advertising materials, and other materials related to such
  12.  * distribution and use acknowledge that the software was developed
  13.  * by the Internet Initiative Japan, Inc.  The name of the
  14.  * IIJ may not be used to endorse or promote products derived
  15.  * from this software without specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  17.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  18.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  *
  20.  *    TODO:
  21.  *        o Add commands for traffic summary, version display, etc.
  22.  *        o Add signal handler for misc controls.
  23.  */
  24. #include "fsm.h"
  25. #include <fcntl.h>
  26. #include <sys/time.h>
  27. #include <termios.h>
  28. #include <sys/ioctl.h>
  29. #include <signal.h>
  30. #include <sys/wait.h>
  31. #include <errno.h>
  32. #include <netdb.h>
  33. #include <sys/socket.h>
  34. #include <arpa/inet.h>
  35. #include "modem.h"
  36. #include "os.h"
  37. #include "hdlc.h"
  38. #include "lcp.h"
  39. #include "ipcp.h"
  40. #include "vars.h"
  41.  
  42. extern void VjInit(), AsyncInit();
  43. extern void AsyncInput(), IpOutput();
  44. extern int  SelectSystem();
  45.  
  46. extern void DecodeCommand(), Prompt();
  47. extern int IsInteractive();
  48. extern struct in_addr ifnetmask;
  49. static void DoLoop(void);
  50.  
  51. static struct termios oldtio;        /* Original tty mode */
  52. static struct termios comtio;        /* Command level tty mode */
  53. static int TermMode;
  54. static int server, update;
  55. struct sockaddr_in ifsin;
  56.  
  57. static void
  58. TtyInit()
  59. {
  60.   struct termios newtio;
  61.   int stat;
  62.  
  63.   stat = fcntl(0, F_GETFL, 0);
  64.   stat |= O_NONBLOCK;
  65.   fcntl(0, F_SETFL, stat);
  66.   newtio = oldtio;
  67.   newtio.c_lflag &= ~(ECHO|ISIG|ICANON);
  68.   newtio.c_iflag = 0;
  69.   newtio.c_oflag &= ~OPOST;
  70.   newtio.c_cc[VEOF] = _POSIX_VDISABLE;
  71.   newtio.c_cc[VINTR] = _POSIX_VDISABLE;
  72.   newtio.c_cc[VMIN] = 1;
  73.   newtio.c_cc[VTIME] = 0;
  74.   newtio.c_cflag |= CS8;
  75.   ioctl(0, TIOCSETA, &newtio);
  76.   comtio = newtio;
  77. }
  78.  
  79. /*
  80.  *  Set tty into command mode. We allow canonical input and echo processing.
  81.  */
  82. static void
  83. TtyCommandMode()
  84. {
  85.   struct termios newtio;
  86.   int stat;
  87.  
  88.   if (!(mode & MODE_INTER))
  89.     return;
  90.   ioctl(0, TIOCGETA, &newtio);
  91.   newtio.c_lflag |= (ECHO|ICANON);
  92.   newtio.c_iflag = oldtio.c_iflag;
  93.   newtio.c_oflag |= OPOST;
  94.   ioctl(0, TIOCSETA, &newtio);
  95.   stat = fcntl(0, F_GETFL, 0);
  96.   stat |= O_NONBLOCK;
  97.   fcntl(0, F_SETFL, stat);
  98.   TermMode = 0;
  99.   Prompt(0);
  100. }
  101.  
  102. /*
  103.  * Set tty into terminal mode which is used while we invoke term command.
  104.  */
  105. void
  106. TtyTermMode()
  107. {
  108.   int stat;
  109.  
  110.   ioctl(0, TIOCSETA, &comtio);
  111.   stat = fcntl(0, F_GETFL, 0);
  112.   stat &= ~O_NONBLOCK;
  113.   fcntl(0, F_SETFL, stat);
  114.   TermMode = 1;
  115. }
  116.  
  117. void
  118. Cleanup(excode)
  119. int excode;
  120. {
  121.   int stat;
  122.  
  123.   OsLinkdown();
  124.   stat = fcntl(0, F_GETFL, 0);
  125.   stat &= ~O_NONBLOCK;
  126.   fcntl(0, F_SETFL, stat);
  127.   ioctl(0, TIOCSETA, &oldtio);
  128.   OsCloseLink(1);
  129.   sleep(1);
  130.   if (mode & MODE_AUTO)
  131.     DeleteIfRoutes(1);
  132.   OsInterfaceDown(1);
  133.   LogPrintf(LOG_PHASE, "PPP Terminated.\n");
  134.   LogClose();
  135.   if (server > 0)
  136.     close(server);
  137.  
  138.   exit(excode);
  139. }
  140.  
  141. static void
  142. Hangup()
  143. {
  144.   LogPrintf(LOG_PHASE, "SIGHUP\n");
  145.   signal(SIGHUP, Hangup);
  146.   Cleanup(EX_HANGUP);
  147. }
  148.  
  149. static void
  150. CloseSession()
  151. {
  152.   LogPrintf(LOG_PHASE, "SIGTERM\n");
  153.   LcpClose();
  154.   Cleanup(EX_TERM);
  155. }
  156.  
  157. void
  158. Usage()
  159. {
  160.   fprintf(stderr, "Usage: ppp [-auto | -direct -dedicated] [system]\n");
  161.   exit(EX_START);
  162. }
  163.  
  164. void
  165. ProcessArgs(int argc, char **argv)
  166. {
  167.   int optc;
  168.   char *cp;
  169.  
  170.   optc = 0;
  171.   while (argc > 0 && **argv == '-') {
  172.     cp = *argv + 1;
  173.     if (strcmp(cp, "auto") == 0)
  174.       mode |= MODE_AUTO;
  175.     else if (strcmp(cp, "direct") == 0)
  176.       mode |= MODE_DIRECT;
  177.     else if (strcmp(cp, "dedicated") == 0)
  178.       mode |= MODE_DEDICATED;
  179.     else
  180.       Usage();
  181.     optc++;
  182.     argv++; argc--;
  183.   }
  184.   if (argc > 1) {
  185.     fprintf(stderr, "specify only one system label.\n");
  186.     exit(EX_START);
  187.   }
  188.   if (argc == 1) dstsystem = *argv;
  189.  
  190.   if (optc > 1) {
  191.     fprintf(stderr, "specify only one mode.\n");
  192.     exit(EX_START);
  193.   }
  194. }
  195.  
  196. static void
  197. Greetings()
  198. {
  199.   printf("User Process PPP. Written by Toshiharu OHNO.\r\n");
  200.   fflush(stdout);
  201. }
  202.  
  203. void
  204. main(argc, argv)
  205. int argc;
  206. char **argv;
  207. {
  208.   int tunno;
  209.   int on = 1;
  210.  
  211.   argc--; argv++;
  212.  
  213.   mode = MODE_INTER;        /* default operation is interactive mode */
  214.   netfd = -1;
  215.   ProcessArgs(argc, argv);
  216.   Greetings();
  217.   GetUid();
  218.   IpcpDefAddress();
  219.  
  220.   if (SelectSystem("default", CONFFILE) < 0) {
  221.     fprintf(stderr, "Warning: No default entry is given in config file.\n");
  222.   }
  223.  
  224.   if (LogOpen())
  225.     exit(EX_START);
  226.  
  227.   if (OpenTunnel(&tunno) < 0) {
  228.     perror("open_tun");
  229.     exit(EX_START);
  230.   }
  231.  
  232.   if (mode & (MODE_AUTO|MODE_DIRECT|MODE_DEDICATED))
  233.     mode &= ~MODE_INTER;
  234.   if (mode & MODE_INTER) {
  235.     printf("Interactive mode\n");
  236.     netfd = 0;
  237.   } else if (mode & MODE_AUTO) {
  238.     printf("Automatic mode\n");
  239.     if (dstsystem == NULL) {
  240.       fprintf(stderr, "Destination system must be specified in auto mode.\n");
  241.       exit(EX_START);
  242.     }
  243.   }
  244.  
  245.   ioctl(0, TIOCGETA, &oldtio);        /* Save original tty mode */
  246.  
  247.   signal(SIGHUP, Hangup);
  248.   signal(SIGTERM, CloseSession);
  249.   signal(SIGINT, CloseSession);
  250.   signal(SIGSEGV, Hangup);
  251.  
  252.   if (dstsystem) {
  253.     if (SelectSystem(dstsystem, CONFFILE) < 0) {
  254.       fprintf(stderr, "Destination system not found in conf file.\n");
  255.       Cleanup(EX_START);
  256.     }
  257.     if ((mode & MODE_AUTO) && DefHisAddress.ipaddr.s_addr == INADDR_ANY) {
  258.       fprintf(stderr, "Must specify dstaddr with auto mode.\n");
  259.       Cleanup(EX_START);
  260.     }
  261.   }
  262.   if (mode & MODE_DIRECT)
  263.     printf("Packet mode enabled.\n");
  264.  
  265. #ifdef notdef
  266.   if (mode & MODE_AUTO) {
  267.     OsSetIpaddress(IpcpInfo.want_ipaddr, IpcpInfo.his_ipaddr, ifnetmask);
  268.   }
  269. #endif
  270.  
  271.   if (!(mode & MODE_INTER)) {
  272.      int port = SERVER_PORT + tunno;
  273.     /*
  274.      *  Create server socket and listen at there.
  275.      */
  276.     server = socket(PF_INET, SOCK_STREAM, 0);
  277.     if (server < 0) {
  278.       perror("socket");
  279.       Cleanup(EX_SOCK);
  280.     }
  281.     ifsin.sin_family = AF_INET;
  282.     ifsin.sin_addr.s_addr = INADDR_ANY;
  283.     ifsin.sin_port = htons(port);
  284.     if (bind(server, (struct sockaddr *) &ifsin, sizeof(ifsin)) < 0) {
  285.       perror("bind");
  286.       if (errno == EADDRINUSE)
  287.     fprintf(stderr, "Wait for a while, then try again.\n");
  288.       Cleanup(EX_SOCK);
  289.     }
  290.     listen(server, 5);
  291.  
  292.     DupLog();
  293.     if (!(mode & MODE_DIRECT)) {
  294.       if (fork())
  295.         exit(0);
  296.     }
  297.     LogPrintf(LOG_PHASE, "Listening at %d.\n", port);
  298. #ifdef DOTTYINIT
  299.     if (mode & (MODE_DIRECT|MODE_DEDICATED)) {
  300. #else
  301.     if (mode & MODE_DIRECT) {
  302. #endif
  303.       TtyInit();
  304.     } else {
  305.       setsid();            /* detach control tty */
  306.     }
  307.   } else {
  308.     server = -1;
  309.     TtyInit();
  310.     TtyCommandMode();
  311.   }
  312.   LogPrintf(LOG_PHASE, "PPP Started.\n");
  313.  
  314.  
  315.   do
  316.    DoLoop();
  317.   while (mode & MODE_DEDICATED);
  318.  
  319.   Cleanup(EX_DONE);
  320. }
  321.  
  322. /*
  323.  *  Turn into packet mode, where we speek PPP.
  324.  */
  325. void
  326. PacketMode()
  327. {
  328.   if (RawModem(modem) < 0) {
  329.     fprintf(stderr, "Not connected.\r\n");
  330.     return;
  331.   }
  332.  
  333.   AsyncInit();
  334.   VjInit();
  335.   LcpInit();
  336.   IpcpInit();
  337.   CcpInit();
  338.   LcpUp();
  339.  
  340.   if (mode & (MODE_DIRECT|MODE_DEDICATED))
  341.     LcpOpen(OPEN_ACTIVE);
  342.   else
  343.     LcpOpen(VarOpenMode);
  344.   if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER) {
  345.     TtyCommandMode();
  346.     fprintf(stderr, "Packet mode.\r\n");
  347.   }
  348. }
  349.  
  350. static void
  351. ShowHelp()
  352. {
  353.   fprintf(stderr, "Following commands are available\r\n");
  354.   fprintf(stderr, " ~p\tEnter to Packet mode\r\n");
  355.   fprintf(stderr, " ~.\tTerminate program\r\n");
  356. }
  357.  
  358. static void
  359. ReadTty()
  360. {
  361.   int n;
  362.   char ch;
  363.   static int ttystate;
  364. #define MAXLINESIZE 200
  365.   char linebuff[MAXLINESIZE];
  366.  
  367. #ifdef DEBUG
  368.   logprintf("termode = %d, netfd = %d, mode = %d\n", TermMode, netfd, mode);
  369. #endif
  370.   if (!TermMode) {
  371.     n = read(netfd, linebuff, sizeof(linebuff)-1);
  372.     if (n > 0)
  373.       DecodeCommand(linebuff, n, 1);
  374.     else {
  375. #ifdef DEBUG
  376.       logprintf("connection closed.\n");
  377. #endif
  378.       close(netfd);
  379.       netfd = -1;
  380.       mode &= ~MODE_INTER;
  381.     }
  382.     return;
  383.   }
  384.  
  385.   /*
  386.    *  We are in terminal mode, decode special sequences
  387.    */
  388.   n = read(0, &ch, 1);
  389. #ifdef DEBUG
  390.   logprintf("got %d bytes\n", n);
  391. #endif
  392.  
  393.   if (n > 0) {
  394.     switch (ttystate) {
  395.     case 0:
  396.       if (ch == '~')
  397.     ttystate++;
  398.       else
  399.     write(modem, &ch, n);
  400.       break;
  401.     case 1:
  402.       switch (ch) {
  403.       case '?':
  404.     ShowHelp();
  405.     break;
  406.       case '-':
  407.     if (loglevel > 0) {
  408.       loglevel--;
  409.       fprintf(stderr, "New loglevel is %d\r\n", loglevel);
  410.     }
  411.     break;
  412.       case '+':
  413.     loglevel++;
  414.     fprintf(stderr, "New loglevel is %d\r\n", loglevel);
  415.     break;
  416. #ifdef DEBUG
  417.       case 'm':
  418.     ShowMemMap();
  419.     break;
  420. #endif
  421.       case 'p':
  422.     /*
  423.      * XXX: Should check carrier.
  424.      */
  425.     if (LcpFsm.state <= ST_CLOSED) {
  426.       VarOpenMode = OPEN_ACTIVE;
  427.       PacketMode();
  428.     }
  429.     break;
  430. #ifdef DEBUG
  431.       case 't':
  432.     ShowTimers();
  433.     break;
  434. #endif
  435.       case '.':
  436.     TermMode = 1;
  437.     TtyCommandMode();
  438.     break;
  439.       default:
  440.     if (write(modem, &ch, n) < 0)
  441.       fprintf(stderr, "err in write.\r\n");
  442.     break;
  443.       }
  444.       ttystate = 0;
  445.       break;
  446.     }
  447.   }
  448. }
  449.  
  450.  
  451. /*
  452.  *  Here, we'll try to detect HDLC frame
  453.  */
  454.  
  455. static char *FrameHeaders[] = {
  456.   "\176\177\175\043",
  457.   "\176\377\175\043",
  458.   "\176\175\137\175\043",
  459.   "\176\175\337\175\043",
  460.   NULL,
  461. };
  462.  
  463. u_char *
  464. HdlcDetect(cp, n)
  465. u_char *cp;
  466. int n;
  467. {
  468.   char *ptr, **hp;
  469.  
  470.   cp[n] = '\0';    /* be sure to null terminated */
  471.   ptr = NULL;
  472.   for (hp = FrameHeaders; *hp; hp++) {
  473.     if (ptr = strstr((char *)cp, *hp))
  474.       break;
  475.   }
  476.   return((u_char *)ptr);
  477. }
  478.  
  479. static struct pppTimer RedialTimer;
  480.  
  481. static void
  482. RedialTimeout()
  483. {
  484.   StopTimer(&RedialTimer);
  485.   LogPrintf(LOG_PHASE, "Redialing timer expired.\n");
  486. }
  487.  
  488. static void
  489. StartRedialTimer()
  490. {
  491.   LogPrintf(LOG_PHASE, "Enter pause for redialing.\n");
  492.   StopTimer(&RedialTimer);
  493.   RedialTimer.state = TIMER_STOPPED;
  494.   RedialTimer.load = REDIAL_PERIOD * SECTICKS;
  495.   RedialTimer.func = RedialTimeout;
  496.   StartTimer(&RedialTimer);
  497. }
  498.  
  499.  
  500. static void
  501. DoLoop()
  502. {
  503.   fd_set rfds, wfds, efds;
  504.   int pri, i, n, wfd;
  505.   struct sockaddr_in hisaddr;
  506.   struct timeval timeout, *tp;
  507.   int ssize = sizeof(hisaddr);
  508.   u_char *cp;
  509.   u_char rbuff[MAX_MRU];
  510.  
  511.   if (mode & MODE_DIRECT) {
  512.     modem = OpenModem(mode);
  513.     fprintf(stderr, "Packet mode enabled\n");
  514.     PacketMode();
  515.   } else if (mode & MODE_DEDICATED) {
  516.     if (!modem)
  517.       modem = OpenModem(mode);
  518.   }
  519.  
  520.   fflush(stdout);
  521.  
  522.   timeout.tv_sec = 0;;
  523.   timeout.tv_usec = 0;
  524.  
  525.   for (;;) {
  526.     IpStartOutput();
  527.     FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds);
  528.     FD_SET(tun_in, &rfds);
  529.     if (server > 0) FD_SET(server, &rfds);
  530.  
  531.     /*  *** IMPORTANT ***
  532.      *
  533.      *  CPU is serviced every TICKUNIT micro seconds.
  534.      *    This value must be chosen with great care. If this values is
  535.      *  too big, it results loss of characters from modem and poor responce.
  536.      *  If this values is too small, ppp process eats many CPU time.
  537.      */
  538.     usleep(TICKUNIT);
  539.     TimerService();
  540.  
  541.     if (modem) {
  542.       FD_SET(modem, &rfds);
  543.       FD_SET(modem, &efds);
  544.       FD_SET(modem, &wfds);
  545.     }
  546.     if (netfd > -1) {
  547.       FD_SET(netfd, &rfds);
  548.       FD_SET(netfd, &efds);
  549.     }
  550.     /*
  551.      *  Normally, slect() will not block because modem is writable.
  552.      *  In AUTO mode, select will block until we find packet from tun.
  553.      *  However, we have to run ourselves while we are in redial wait state.
  554.      */
  555.     tp = (RedialTimer.state == TIMER_RUNNING)? &timeout : NULL;
  556.     i = select(tun_in+10, &rfds, &wfds, &efds, tp);
  557.     if (i == 0) {
  558.       continue;
  559.     }
  560.     if (i < 0) {
  561.       perror("select");
  562.       break;
  563.     }
  564.     if ((netfd > 0 && FD_ISSET(netfd, &efds)) || FD_ISSET(modem, &efds)) {
  565.       logprintf("Exception detected.\n");
  566.       break;
  567.     }
  568.  
  569.     if (server > 0 && FD_ISSET(server, &rfds)) {
  570. #ifdef DEBUG
  571.       logprintf("connected to client.\n");
  572. #endif
  573.       wfd = accept(server, (struct sockaddr *)&hisaddr, &ssize);
  574.       if (netfd > 0) {
  575.     write(wfd, "already in use.\n", 16);
  576.     close(wfd);
  577.     continue;
  578.       } else
  579.     netfd = wfd;
  580.       if (dup2(netfd, 1) < 0)
  581.     perror("dup2");
  582.       mode |= MODE_INTER;
  583.       Greetings();
  584.       (void) IsInteractive();
  585.       Prompt(0);
  586.     }
  587.  
  588.     if ((mode & MODE_INTER) && FD_ISSET(netfd, &rfds)) {
  589.       /* something to read from tty */
  590.       ReadTty();
  591.     }
  592.     if (modem) {
  593.       if (FD_ISSET(modem, &wfds)) {    /* ready to write into modem */
  594.      ModemStartOutput(modem);
  595.       }
  596.       if (FD_ISSET(modem, &rfds)) {    /* something to read from modem */
  597.     n = read(modem, rbuff, sizeof(rbuff));
  598.     if ((mode & MODE_DIRECT) && n <= 0) {
  599.       DownConnection();
  600.     } else
  601.           LogDumpBuff(LOG_ASYNC, "ReadFromModem", rbuff, n);
  602.  
  603.     if (LcpFsm.state <= ST_CLOSED) {
  604.       /*
  605.        *  In dedicated mode, we just discard input until LCP is started.
  606.        */
  607.       if (!(mode & MODE_DEDICATED)) {
  608.         cp = HdlcDetect(rbuff, n);
  609.         if (cp) {
  610.           /*
  611.            * LCP packet is detected. Turn ourselves into packet mode.
  612.            */
  613.           if (cp != rbuff) {
  614.             write(1, rbuff, cp - rbuff);
  615.             write(1, "\r\n", 2);
  616.           }
  617.           PacketMode();
  618. #ifdef notdef
  619.           AsyncInput(cp, n - (cp - rbuff));
  620. #endif
  621.         } else
  622.           write(1, rbuff, n);
  623.       }
  624.     } else {
  625.       if (n > 0)
  626.         AsyncInput(rbuff, n);
  627. #ifdef notdef
  628.       continue;            /* THIS LINE RESULT AS POOR PERFORMANCE */
  629. #endif
  630.     }
  631.       }
  632.     }
  633.     if (FD_ISSET(tun_in, &rfds)) {    /* something to read from tun */
  634.       /*
  635.        *  If there are many packets queued, wait until they are drained.
  636.        */
  637.       if (ModemQlen() > 5)
  638.     continue;
  639.  
  640.       n = read(tun_in, rbuff, sizeof(rbuff));
  641.       if (n < 0) {
  642.     perror("read from tun");
  643.     continue;
  644.       }
  645.       /*
  646.        *  Process on-demand dialup. Output packets are queued within tunnel
  647.        *  device until IPCP is opened.
  648.        */
  649.       if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) {
  650.     pri = PacketCheck(rbuff, n, 2);
  651.     if (pri >= 0) {
  652.       if (RedialTimer.state == TIMER_RUNNING) {
  653.         /*
  654.          * We are in redial wait state. Ignore packet.
  655.          */
  656.         continue;
  657.       }
  658.       modem = OpenModem(mode);
  659. #ifdef DEBUG
  660.       logprintf("going to dial: modem = %d\n", modem);
  661. #endif
  662.       if (modem < 0) {
  663.         printf("failed to open modem.\n");
  664.         Cleanup(EX_MODEM);
  665.       }
  666.  
  667.       if (DialModem()) {
  668.         sleep(1);        /* little pause to allow peer starts */
  669.         ModemTimeout();
  670.         PacketMode();
  671.       } else {
  672. #ifdef notdef
  673.         Cleanup(EX_DIAL);
  674. #endif
  675.         CloseModem();
  676.         /* Dial failed. Keep quite during redial wait period. */
  677.         /* XXX: We shoud implement re-dial */
  678.         StartRedialTimer();
  679.         continue;
  680.       }
  681.       IpEnqueue(pri, rbuff, n);
  682.     }
  683.     continue;
  684.       }
  685.       pri = PacketCheck(rbuff, n, 1);
  686.       if (pri >= 0)
  687.     IpEnqueue(pri, rbuff, n);
  688.     }
  689.   }
  690.   logprintf("job done.\n");
  691. }
  692.